#include "ResourceDecoder.h"
#include "FString.h"
#include "FileData.h"
#include "Log.h"

#include "Image.h"
#include "TGALoader.h"


bool TGALoader::checkExtension (const FString& theName)
{
	FString ext = FString::getExtension (theName).toLower();
	return ext == "tga";
}



typedef struct _TargaHeader {
	unsigned char 	id_length, colormap_type, image_type;
	unsigned short	colormap_index, colormap_length;
	unsigned char	colormap_size;
	unsigned short	x_origin, y_origin, width, height;
	unsigned char	pixel_size, attributes;
} TargaHeader;



IBase *TGALoader::decode(FileData *file)
{
	int		columns, rows, numPixels;
	byte	*pixbuf;
	int		row, column;
	byte	*buf_p;
	byte	*buffer;

	int width; 
	int height;

	TargaHeader	targa_header;
	byte		*targa_rgba;
	byte		*pic;

	pic = NULL;

	buffer = (byte*) file->getPtr();

	if (!buffer) return NULL;

	buf_p = buffer;

	targa_header.id_length = *buf_p++;
	targa_header.colormap_type = *buf_p++;
	targa_header.image_type = *buf_p++;
	
	targa_header.colormap_index = *(short *)buf_p;
	buf_p += 2;
	targa_header.colormap_length = *(short *)buf_p;
	buf_p += 2;
	targa_header.colormap_size = *buf_p++;
	targa_header.x_origin = *(short *)buf_p;
	buf_p += 2;
	targa_header.y_origin = *(short *)buf_p;
	buf_p += 2;
	targa_header.width = *(short *)buf_p;
	buf_p += 2;
	targa_header.height = *(short *)buf_p;
	buf_p += 2;
	targa_header.pixel_size = *buf_p++;
	targa_header.attributes = *buf_p++;

	if (targa_header.image_type!=2 
		&& targa_header.image_type!=10
		&& targa_header.image_type != 3 ) 
	{
		sysLog << "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported" << logEndl;
	}

	if ( targa_header.colormap_type != 0 )
	{
		sysLog << "LoadTGA: colormaps not supported" << logEndl;
	}

	if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
	{
		sysLog << "LoadTGA: Only 32 or 24 bit images supported (no colormaps)" << logEndl;
	}

	columns = targa_header.width;
	rows = targa_header.height;
	numPixels = columns * rows;

	width	= columns;
	height	= rows;

	targa_rgba = new byte[numPixels*4];
	pic = targa_rgba;

	if (targa_header.id_length != 0)
		buf_p += targa_header.id_length;  // skip TARGA image comment
	
	if ( targa_header.image_type==2 || targa_header.image_type == 3 )
	{ 
		// Uncompressed RGB or gray scale image
		for(row=rows-1; row>=0; row--) 
		{
			pixbuf = targa_rgba + row*columns*4;
			for(column=0; column<columns; column++) 
			{
				unsigned char red,green,blue,alphabyte;
				switch (targa_header.pixel_size) 
				{
					
				case 8:
					blue = *buf_p++;
					green = blue;
					red = blue;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = 255;
					break;

				case 24:
					blue = *buf_p++;
					green = *buf_p++;
					red = *buf_p++;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = 255;
					break;
				case 32:
					blue = *buf_p++;
					green = *buf_p++;
					red = *buf_p++;
					alphabyte = *buf_p++;
					*pixbuf++ = red;
					*pixbuf++ = green;
					*pixbuf++ = blue;
					*pixbuf++ = alphabyte;
					break;
				default:
					sysLog << "LoadTGA: illegal pixel_size " << targa_header.pixel_size << " in file " << file->getName() << logEndl;
					break;
				}
			}
		}
	}
	else if (targa_header.image_type==10) 
	{   // Runlength encoded RGB images
		unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;

		red = 0;
		green = 0;
		blue = 0;
		alphabyte = 0xff;

		for(row=rows-1; row>=0; row--) 
		{
			pixbuf = targa_rgba + row*columns*4;
			for(column=0; column<columns; ) 
			{
				packetHeader= *buf_p++;
				packetSize = 1 + (packetHeader & 0x7f);
				if (packetHeader & 0x80) 
				{        // run-length packet
					switch (targa_header.pixel_size) 
					{
						case 24:
								blue = *buf_p++;
								green = *buf_p++;
								red = *buf_p++;
								alphabyte = 255;
								break;
						case 32:
								blue = *buf_p++;
								green = *buf_p++;
								red = *buf_p++;
								alphabyte = *buf_p++;
								break;
						default:
							sysLog << "LoadTGA: illegal pixel_size " << targa_header.pixel_size << " in file " << file->getName() << logEndl;
							break;
					}
	
					for(j=0;j<packetSize;j++) 
					{
						*pixbuf++=red;
						*pixbuf++=green;
						*pixbuf++=blue;
						*pixbuf++=alphabyte;
						column++;
						if (column==columns) 
						{ // run spans across rows
							column=0;
							if (row>0)
								row--;
							else
								goto breakOut;
							pixbuf = targa_rgba + row*columns*4;
						}
					}
				}
				else 
				{                            // non run-length packet
					for(j=0;j<packetSize;j++) 
					{
						switch (targa_header.pixel_size) 
						{
							case 24:
									blue = *buf_p++;
									green = *buf_p++;
									red = *buf_p++;
									*pixbuf++ = red;
									*pixbuf++ = green;
									*pixbuf++ = blue;
									*pixbuf++ = 255;
									break;
							case 32:
									blue = *buf_p++;
									green = *buf_p++;
									red = *buf_p++;
									alphabyte = *buf_p++;
									*pixbuf++ = red;
									*pixbuf++ = green;
									*pixbuf++ = blue;
									*pixbuf++ = alphabyte;
									break;
							default:
								sysLog << "LoadTGA: illegal pixel_size " << targa_header.pixel_size << " in file " << file->getName() << logEndl;
								break;
						}
						column++;
						if (column==columns) 
						{ // pixel packet run spans across rows
							column=0;
							if (row>0)
								row--;
							else
								goto breakOut;
							pixbuf = targa_rgba + row*columns*4;
						}						
					}
				}
			}
			breakOut:;
		}
	}

  // instead we just print a warning
  if (targa_header.attributes & 0x20) 
  {
	sysLog << "WARNING: " << file->getName() << " TGA file header declares top-down image, ignoring" << logEndl;
  }

	Image *image = new Image (file->getName(), width, height, 32, IMAGE_FORMAT_RGBA8888);
	image->setData (pic);

	return image;

}